// define a local copy
var jsoq,
// keep a reference for some core methods
    toString = Object.prototype.toString,
//re to match a lambda expression (ie: "x=> x / 2")
    lambdaMatch = /\s*\(?([\w*\,]*)\)?[\s|\S]*\=>(\s*[\s|\S]*([.|\S|\s]*))\s*/,
// expression compiler error message
    exprCompileError = "expression is invalid:";


jsoq = {
    // returns an array
    // param: obj
    // paramtype: Array, List, Enumerator, null
    getArray: function(obj) {
        // return a new array if no param received
        return !obj ? []
        // if obj is an array, return obj
                : jsoq.isArray(obj) ? obj
        // if obj is a list, call getArray() from List object, which is less expensive than Enumrator.toArray()
                : obj.isList ? obj.toArray()
        // or get array from obj Enumerator
                : obj.toEnum ? obj.toEnum().toArray()
        // or return a new array with obj
                : [obj];
    },

    // is obj a function?
    isFunction: function(obj) {
        return toString.call(obj) === "[object Function]";
    },

    // is obj an array?
    isArray: function(obj) {
        return toString.call(obj) === "[object Array]";
    },

    // extends an object prototype
    proto: function(target, members) {
        for (var name in members) {
            if (members.hasOwnProperty(name))
                target.prototype[name] = members[name];
        }
    },

    // unique object id counter
    guid: 0,

    // save a pointer to Base object
    $class: createBaseClass(),

    // create an inheritable class
    // uses the Base class of Dean Edwards, unmodified.
    define: function(members, _static) {
        return this.$class.extend(members, _static);
    },

    ready: [],

    init: function(callback) {
        if (this.isFunction(callback))
            this.ready.push(callback);
        else {
            for (var i = 0, len = this.ready.length; i < len; i++)
                this.ready[i].call(this);
        }
    },

    //compile a lambda expression and returns his equilavent function()
    expr: function(expr) {
        var splitted = expr.match(lambdaMatch);

        if (splitted.length < 3) throw exprCompileError;

        var params = splitted[1].replace(/^\s*|\s(?=\s)|\s*$/g, '');
        var body = splitted[2];

        //check if expression has just 1 statement;
        if (body.indexOf(';') < 2) {
            // prepend a return if not already there.
            body = ((!/\s*return\s+/.test(body)) ? "return " : "") + body;
        }

        var result = eval('fn = function(' + params + ') {' + body + '}');
        result.guid = ++jsoq.guid;
        return result;
    },

    //returns a fuction() representing the given expr 
    //param: expr
    //paramtype: String expression, Function() ou a Expression object
    getExpr: function(expr) {
        //if nothing received, return null
        return !expr ? null
        //return expr if it is a Function
            : jsoq.isFunction(expr) ? expr
        //if expr is a string, then compile it
            : typeof expr === "string" ? jsoq.expr(expr)
        //if expr is a Expression object, return run method
            : expr.isExpression ? expr.run
        // or null
            : null;
    },

    //extends jsoq object
    extend: function(members) {
        for (var name in members) {
            if (members.hasOwnProperty(name)) {
                if (name === "init")
                    this.init(members[name]);
                else
                    this[name] = members[name];
            }
        }
    }
};

